/*******************************************************************************
 * Copyright (c) 2011 TXT e-solutions SpA
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * This work was performed within the IoT_at_Work Project
 * and partially funded by the European Commission's
 * 7th Framework Programme under the research area ICT-2009.1.3
 * Internet of Things and enterprise environments.
 *
 *
 * Authors:
 *     Cristoforo Seccia (TXT e-solutions SpA)
 *
 * Contributors:
 *      Domenico Rotondi (TXT e-solutions SpA)
 *******************************************************************************/
package it.txt.access.capability.factory;

import it.txt.access.capability.factory.values.root.RootCapabilityValues;
import it.txt.access.capability.factory.values.root.RootCapabilityAction;
import it.txt.access.capability.factory.values.CapabilityAction;
import it.txt.access.capability.factory.values.CapabilityValues;
import it.txt.access.capability.commons.schema.validation.CapabilitySchemaValidationHandlerException;
import it.txt.access.capability.commons.identifier.IdentifierGenerator;
import it.txt.access.capability.commons.schema.CapabilityNamespacesMapper;
import it.txt.access.capability.commons.schema.factory.CapabilitySchemasJaxbFaxtory;
import it.txt.access.capability.commons.schema.factory.CapabilitySchemasLoader;
import it.txt.access.capability.schema.AccessRightType;
import it.txt.access.capability.schema.AccessRightsCapabilityRevocationServiceType;
import it.txt.access.capability.schema.AccessRightsCapabilityType;
import it.txt.access.capability.schema.AccessRightsType;
import it.txt.access.capability.schema.ObjectFactory;
import it.txt.access.capability.schema.SubjectType;
import it.txt.access.capability.commons.utils.CalendarUtils;
import it.txt.access.capability.commons.utils.DOMUtils;
import java.io.File;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.parsers.ParserConfigurationException;
import oasis.names.tc.saml._2_0.assertion.ActionType;
import oasis.names.tc.saml._2_0.assertion.NameIDType;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.transform.dom.DOMSource;
import org.w3._2000._09.xmldsig_.X509IssuerSerialType;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
import static it.txt.access.capability.factory.CapabilitySchemaFactoryException.*;

/**
 *
 * @author Cristoforo Seccia (TXT e-solutions SpA)
 */
public class CapabilitySchemaFactory {

    private static final Logger LOGGER = Logger.getLogger(CapabilitySchemaFactory.class.getName());
    public static final String JAXB_CONTEXT = "it.txt.access.capability.schema";
    public static final String SIGN_DOCUMENT_AFTER_NODE = "Issuer";
    public static final int ACCESS_CAPABILITY_VERSION = 3;
    public static final String ACTION_TYPE_NAMESPACE =
            CapabilityNamespacesMapper.ACCESS_RIGHTS_CAPABILITY_QNAME.getNamespaceURI() +
            "/AccessRightsCapability/ActionRights";
    private final CapabilitySchemasJaxbFaxtory wrapper;
    private final ObjectFactory accessRightsFactory;
    private static CapabilitySchemaFactory factory;
    
    
    public static void main(String[] args) throws CapabilitySchemaFactoryException {
        String capabilitySchemasRootPath = "D:\\Developing\\NetBeans\\Maven2Projects\\TXTAccess\\TXTAccess_BIN\\Schemas";
        LOGGER.log(Level.INFO, "*********************************************");
        LOGGER.log(Level.INFO, "*********************************************");
        CapabilitySchemaFactory.getInstance(capabilitySchemasRootPath);
        LOGGER.log(Level.INFO, "*********************************************");
        LOGGER.log(Level.INFO, "*********************************************");
    }

    private CapabilitySchemaFactory(String capabilitySchemasRootPath) throws CapabilitySchemaFactoryException{
        try {
            CapabilitySchemasLoader loader = new CapabilitySchemasLoader();
            wrapper = new CapabilitySchemasJaxbFaxtory(
                    loader.loadCapabilityEntities(capabilitySchemasRootPath),
                    new CapabilityNamespacesMapper(), AccessRightsCapabilityType.class);
            accessRightsFactory = new ObjectFactory();            
        } catch (JAXBException ex) {
            throw new CapabilitySchemaFactoryException(ex.getMessage(), ex);
        } catch (SAXException ex) {
            throw new CapabilitySchemaFactoryException(ex.getMessage(), ex);
        }
    }

    public synchronized static CapabilitySchemaFactory getInstance(String capabilitySchemasRootPath)
            throws CapabilitySchemaFactoryException {
        if(factory == null){
            factory = new CapabilitySchemaFactory(capabilitySchemasRootPath);
        }
        return factory;
    }

    /**
     * Warning! Use this method only if the CapabilitySchemaFactory has been 
     * initialized with one of the parameterized 'getInstance(...)' method.
     */
    public synchronized static CapabilitySchemaFactory getInstance() throws CapabilitySchemaFactoryException {
        if(factory == null){
            throw new CapabilitySchemaFactoryException("CapabilitySchemaFactory has not been initialized");
        }
        return factory;
    }

    public synchronized AccessRightsCapabilityType createAccessRights(File file) throws CapabilitySchemaFactoryException, CapabilitySchemaValidationHandlerException {
        try {
            wrapper.getCapabilitySchemaValidationHandler().invalidateValidationEvents();
            AccessRightsCapabilityType capabilityType = (AccessRightsCapabilityType) wrapper.getUnmarshaller().unmarshal(file);
            if (!wrapper.getCapabilitySchemaValidationHandler().getValidationEvents().isEmpty()) {
                throw new CapabilitySchemaValidationHandlerException(
                        wrapper.getCapabilitySchemaValidationHandler().getValidationEvents(),
                        "Capability Validation Schema Errors.");
            }
            return capabilityType;
        } catch (JAXBException ex) {
            throw new CapabilitySchemaFactoryException(PARSE_CAPABILITY_ERROR, ex);
        } catch (NullPointerException ex) {
            throw new CapabilitySchemaFactoryException(INIT_CREATOR, ex);
        }
    }

    public synchronized AccessRightsCapabilityType createAccessRights(DOMSource source) throws CapabilitySchemaFactoryException, CapabilitySchemaValidationHandlerException {
        try {
            wrapper.getCapabilitySchemaValidationHandler().invalidateValidationEvents();
            AccessRightsCapabilityType capabilityType = (AccessRightsCapabilityType) wrapper.getUnmarshaller().unmarshal(source);
            if (!wrapper.getCapabilitySchemaValidationHandler().getValidationEvents().isEmpty()) {
                throw new CapabilitySchemaValidationHandlerException(
                        wrapper.getCapabilitySchemaValidationHandler().getValidationEvents(),
                        "Capability Validation Schema Errors.");
            }
            return capabilityType;
        } catch (JAXBException ex) {
            throw new CapabilitySchemaFactoryException(PARSE_CAPABILITY_ERROR, ex);
        } catch (NullPointerException ex) {
            throw new CapabilitySchemaFactoryException(INIT_CREATOR, ex);
        }
    }

    public synchronized Document createAccessRightsDocument(AccessRightsCapabilityType capability) throws CapabilitySchemaFactoryException, CapabilitySchemaValidationHandlerException {
        try {
            wrapper.getCapabilitySchemaValidationHandler().invalidateValidationEvents();
            Document capabilityDocument = DOMUtils.newDocument();
            wrapper.getMarshaller().setProperty(Marshaller.JAXB_FRAGMENT, Boolean.FALSE);
            wrapper.getMarshaller().marshal(capability, capabilityDocument);
            wrapper.getCapabilitySchemaValidationHandler().logValidationEvents();
//            if (!wrapper.getCapabilitySchemaValidationHandler().getValidationEvents().isEmpty()) {
//                throw new CapabilitySchemaValidationHandlerException(
//                        wrapper.getCapabilitySchemaValidationHandler().getValidationEvents(),
//                        "Capability Validation Schema Errors.");
//            }
            return capabilityDocument;
        } catch (ParserConfigurationException ex) {
            throw new CapabilitySchemaFactoryException(PARSE_CAPABILITY_ERROR, ex);
        } catch (JAXBException ex) {
            throw new CapabilitySchemaFactoryException(PARSE_CAPABILITY_ERROR, ex);
        } catch (NullPointerException ex) {
            throw new CapabilitySchemaFactoryException(INIT_CREATOR, ex);
        }
    }

    public synchronized Document createAccessRightsFragmentDocument(AccessRightsCapabilityType capability) throws CapabilitySchemaFactoryException {
        try {
            wrapper.getCapabilitySchemaValidationHandler().invalidateValidationEvents();
            Document capabilityDocument = DOMUtils.newDocument();
            wrapper.getMarshaller().setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
            wrapper.getMarshaller().marshal(capability, capabilityDocument);
            wrapper.getCapabilitySchemaValidationHandler().logValidationEvents();
            return capabilityDocument;
        } catch (ParserConfigurationException ex) {
            throw new CapabilitySchemaFactoryException(PARSE_CAPABILITY_ERROR, ex);
        } catch (JAXBException ex) {
            throw new CapabilitySchemaFactoryException(PARSE_CAPABILITY_ERROR, ex);
        } catch (NullPointerException ex) {
            throw new CapabilitySchemaFactoryException(INIT_CREATOR, ex);
        }
    }

    public synchronized AccessRightsCapabilityType createAccessRights(RootCapabilityValues values) throws CapabilitySchemaFactoryException {

        AccessRightsCapabilityType newCapability = accessRightsFactory.createAccessRightsCapabilityType();

        //Set the version
        newCapability.setVersion(String.valueOf(ACCESS_CAPABILITY_VERSION));

        //Set the issuer
        NameIDType issuerNameID = new NameIDType();
        issuerNameID.setValue(values.getPolicyIssuer());
        newCapability.setIssuer(issuerNameID);

        //Set the issue istant
        Date now = new Date();
        XMLGregorianCalendar issueIstant = null;
        try {
            issueIstant = CalendarUtils.newXmlGregorianCalendarFromDateAndTime(now, now);
        } catch (DatatypeConfigurationException ex) {
            throw new CapabilitySchemaFactoryException(ex.getMessage(), ex);
        }
        XMLGregorianCalendar issueIstantUTC = issueIstant.normalize();
        newCapability.setIssueInstant(issueIstantUTC);

        //Set the assignee
        SubjectType subject = new SubjectType();
        NameIDType nameIDType = new NameIDType();
        nameIDType.setValue(values.getPolicyAssignee());
        subject.setSubjectID(nameIDType);
        newCapability.setSubject(subject);

        //Set the revocation services list
        List<String> services = values.getPolicyRevopcationServices();
        AccessRightsCapabilityRevocationServiceType arcrs =
                accessRightsFactory.createAccessRightsCapabilityRevocationServiceType();
        for (String service : services) {
            arcrs.getRevocationServiceEndPoint().add(service);
        }
        newCapability.setAccessRightsCapabilityRevocationService(arcrs);

        //Set the Access Rights Capability ID
        try {
            newCapability.setAccessRightsCapabilityID(
                    IdentifierGenerator.generateIdentifier());
        } catch (NoSuchAlgorithmException ex) {
            throw new CapabilitySchemaFactoryException(ex.getMessage(), ex);
        }

        //Set the resource ID
        newCapability.setResourceID(values.getPolicyResourceID());

        //Set the Access Rights
        AccessRightsType arts = accessRightsFactory.createAccessRightsType();
        RootCapabilityAction actions[] = values.getRootCapabilityActions();
        for (RootCapabilityAction rootCapabilityAction : actions) {
            ActionType actionType = new ActionType();
            actionType.setValue(rootCapabilityAction.getValue());
            actionType.setNamespace(ACTION_TYPE_NAMESPACE);
            AccessRightType art = accessRightsFactory.createAccessRightType();
            art.setPermittedAction(actionType);
            AccessRightType.Delegable delegable = new AccessRightType.Delegable();
            delegable.setValue(rootCapabilityAction.isDelegable());
            art.setDelegable(delegable);
            arts.getAccessRight().add(art);
        }
        newCapability.setAccessRights(arts);

        //Create the not before date
        XMLGregorianCalendar notBeforeDateTime = null;
        try {
            notBeforeDateTime = CalendarUtils.newXmlGregorianCalendarFromDateAndTime(
                    values.getPolicyNotBeforeDate(),
                    values.getPolicyNotBeforeTime());
        } catch (DatatypeConfigurationException ex) {
            throw new CapabilitySchemaFactoryException(ex.getMessage(), ex);
        }
        XMLGregorianCalendar notBeforeDateTimeUTC = notBeforeDateTime.normalize();

        //Create the not after date
        XMLGregorianCalendar notAfterDateTime = null;
        try {
            notAfterDateTime = CalendarUtils.newXmlGregorianCalendarFromDateAndTime(
                    values.getPolicyNotOrOnAfterDate(),
                    values.getPolicyNotOrOnAfterTime());
        } catch (DatatypeConfigurationException ex) {
            throw new CapabilitySchemaFactoryException(ex.getMessage(), ex);
        }
        XMLGregorianCalendar notAfterDateTimeUTC = notAfterDateTime.normalize();

        AccessRightsCapabilityType.ValidityCondition validityCondition =
                accessRightsFactory.createAccessRightsCapabilityTypeValidityCondition();

        validityCondition.setNotBefore(getReallyCapabilityNotBefore(
                notBeforeDateTimeUTC, issueIstantUTC));

        validityCondition.setNotOnOrAfter(notAfterDateTimeUTC);

        newCapability.setValidityCondition(validityCondition);

        //Set an empty Issuer Rights capability
        AccessRightsCapabilityType.IssuerAccessRightsCapability iarc =
                new AccessRightsCapabilityType.IssuerAccessRightsCapability();

        newCapability.setIssuerAccessRightsCapability(iarc);

        //Return the Access Rights Capability object
        return newCapability;
    }

    public synchronized AccessRightsCapabilityType createAccessRights(AccessRightsCapabilityType innerCapability, CapabilityValues values) throws CapabilitySchemaFactoryException {

        AccessRightsCapabilityType newCapability = accessRightsFactory.createAccessRightsCapabilityType();

        //Set the version
        newCapability.setVersion(String.valueOf(ACCESS_CAPABILITY_VERSION));

        //Set the issue istant
        Date now = new Date();
        XMLGregorianCalendar issueIstant = null;
        try {
            issueIstant = CalendarUtils.newXmlGregorianCalendarFromDateAndTime(now, now);
        } catch (DatatypeConfigurationException ex) {
            throw new CapabilitySchemaFactoryException(ex.getMessage(), ex);
        }
        XMLGregorianCalendar issueIstantUTC = issueIstant.normalize();
        newCapability.setIssueInstant(issueIstantUTC);

        //Set the issuer
        NameIDType issuerNameID = new NameIDType();
        issuerNameID.setValue(innerCapability.getSubject().getSubjectID().getValue());
        newCapability.setIssuer(issuerNameID);

        //Set the assignee
        SubjectType subject = new SubjectType();
        NameIDType nameIDType = new NameIDType();
        nameIDType.setValue(values.getPolicyAssignee());
        subject.setSubjectID(nameIDType);

        //Set the Subject Key Info if exists
        if (values.getX509SubjectInfo() != null) {
            SubjectType.SubjectKeyInfo keyInfo = new SubjectType.SubjectKeyInfo();
            X509IssuerSerialType issuerSerialType = new X509IssuerSerialType();
            issuerSerialType.setX509IssuerName(values.getX509SubjectInfo().getX509IssuerName());
            issuerSerialType.setX509SerialNumber(values.getX509SubjectInfo().getX509SerialNumber());
            keyInfo.setX509IssuerSerial(issuerSerialType);
            subject.setSubjectKeyInfo(keyInfo);
        }

        newCapability.setSubject(subject);

        //Set the revocation services list
        List<String> services =
                innerCapability.getAccessRightsCapabilityRevocationService().getRevocationServiceEndPoint();
        AccessRightsCapabilityRevocationServiceType arcrs =
                accessRightsFactory.createAccessRightsCapabilityRevocationServiceType();
        for (String service : services) {
            arcrs.getRevocationServiceEndPoint().add(service);
        }
        newCapability.setAccessRightsCapabilityRevocationService(arcrs);

        //Set the Access Rights Capability ID
        try {
            newCapability.setAccessRightsCapabilityID(
                    IdentifierGenerator.generateIdentifier());
        } catch (NoSuchAlgorithmException ex) {
            throw new CapabilitySchemaFactoryException(ex.getMessage(), ex);
        }

        //Set the resource ID
        newCapability.setResourceID(innerCapability.getResourceID());

        //Set the Access Rights
        AccessRightsType arts = accessRightsFactory.createAccessRightsType();
        CapabilityAction actions[] = values.getCapabilityActions();
        for (CapabilityAction capabilityAction : actions) {
            if (!capabilityAction.isAuthorize() && !capabilityAction.isDelegable()) {
                continue;
            } else {
                ActionType actionType = new ActionType();
                actionType.setValue(capabilityAction.getValue());
                actionType.setNamespace(ACTION_TYPE_NAMESPACE);
                AccessRightType art = accessRightsFactory.createAccessRightType();
                art.setPermittedAction(actionType);
                AccessRightType.Delegable delegable = new AccessRightType.Delegable();
                delegable.setValue(capabilityAction.isDelegable());
                art.setDelegable(delegable);
                arts.getAccessRight().add(art);
            }
        }
        newCapability.setAccessRights(arts);

        //Create the not before date
        XMLGregorianCalendar notBeforeDateTime = null;
        try {
            notBeforeDateTime =
                    CalendarUtils.newXmlGregorianCalendarFromDateAndTime(
                    values.getPolicyNotBeforeDate(),
                    values.getPolicyNotBeforeTime());
        } catch (DatatypeConfigurationException ex) {
            throw new CapabilitySchemaFactoryException(ex.getMessage(), ex);
        }
        XMLGregorianCalendar notBeforeDateTimeUTC = notBeforeDateTime.normalize();

        XMLGregorianCalendar inner_notBeforeDateTimeUTC =
                innerCapability.getValidityCondition().getNotBefore().normalize();

        check_CapabilityN_NotBefore_And_ValuesN1_NotBefore(notBeforeDateTimeUTC,
                inner_notBeforeDateTimeUTC);

        //create the not after date
        XMLGregorianCalendar notAfterDateTime = null;
        try {
            notAfterDateTime =
                    CalendarUtils.newXmlGregorianCalendarFromDateAndTime(
                    values.getPolicyNotOrOnAfterDate(),
                    values.getPolicyNotOrOnAfterTime());

        } catch (DatatypeConfigurationException ex) {
            throw new CapabilitySchemaFactoryException(ex.getMessage(), ex);
        }
        XMLGregorianCalendar notAfterDateTimeUTC = notAfterDateTime.normalize();

        XMLGregorianCalendar inner_notAfterDateTimeUTC =
                innerCapability.getValidityCondition().getNotOnOrAfter().normalize();

        check_CapabilityN_NotOrOnAfter_And_ValuesN1_NotOrOnAfter(notAfterDateTimeUTC,
                inner_notAfterDateTimeUTC);

        AccessRightsCapabilityType.ValidityCondition validityCondition =
                accessRightsFactory.createAccessRightsCapabilityTypeValidityCondition();

        validityCondition.setNotBefore(getReallyCapabilityNotBefore(
                notBeforeDateTimeUTC, issueIstantUTC));

        validityCondition.setNotOnOrAfter(notAfterDateTimeUTC);

        newCapability.setValidityCondition(validityCondition);

        //Set the issuer Access Rights Capability
        AccessRightsCapabilityType.IssuerAccessRightsCapability iarc =
                new AccessRightsCapabilityType.IssuerAccessRightsCapability();

        iarc.setAccessRightsCapability(innerCapability);

        newCapability.setIssuerAccessRightsCapability(iarc);

        return newCapability;
    }

    private synchronized void check_CapabilityN_NotBefore_And_ValuesN1_NotBefore(XMLGregorianCalendar capability_notBefore,
            XMLGregorianCalendar inner_notBefore) throws CapabilitySchemaFactoryException {

        int check_CapabilityN_NotBefore_And_ValuesN1_NotBefore =
                CalendarUtils.comapareDataToAnotherDate(capability_notBefore,
                inner_notBefore);

        switch (check_CapabilityN_NotBefore_And_ValuesN1_NotBefore) {
            case CalendarUtils.AFTER_THAN: {
                String msg = "The 'Not Before' date of the created capability ("
                + new SimpleDateFormat(CalendarUtils.DATE_PRINT_PATTERN).format(
                        capability_notBefore.toGregorianCalendar().getTime())
                + ") is greater than 'Not Before' date of the authorising capability ("
                + new SimpleDateFormat(CalendarUtils.DATE_PRINT_PATTERN).format(
                        inner_notBefore.toGregorianCalendar().getTime()) + ")";
                LOGGER.fine(msg);
                break;
            }
            case CalendarUtils.BEFORE_THAN: {
                String msg = "The 'Not Before' date of the created capability ("
                + new SimpleDateFormat(CalendarUtils.DATE_PRINT_PATTERN).format(
                        capability_notBefore.toGregorianCalendar().getTime())
                + ") is earlier than 'Not Before' date of the authorising capability ("
                + new SimpleDateFormat(CalendarUtils.DATE_PRINT_PATTERN).format(
                        inner_notBefore.toGregorianCalendar().getTime()) + ")";
                LOGGER.severe(msg);
                throw new CapabilitySchemaFactoryException(msg);
            }
            case CalendarUtils.EQUAL_TO: {
                String msg = "The 'Not Before' date of the created capability ("
                + new SimpleDateFormat(CalendarUtils.DATE_PRINT_PATTERN).format(
                        capability_notBefore.toGregorianCalendar().getTime())
                + ") is equal to the 'Not Before' date of the authorising capability ("
                + new SimpleDateFormat(CalendarUtils.DATE_PRINT_PATTERN).format(
                        inner_notBefore.toGregorianCalendar().getTime()) + ")";
                throw new CapabilitySchemaFactoryException(msg);
            }
        }
    }

    private synchronized void check_CapabilityN_NotOrOnAfter_And_ValuesN1_NotOrOnAfter(XMLGregorianCalendar capability_notOrOnAfter,
            XMLGregorianCalendar inner_notOrOnAfter) throws CapabilitySchemaFactoryException {

        int check_CapabilityN_NotOrOnAfter_And_ValuesN1_NotOrOnAfter =
                CalendarUtils.comapareDataToAnotherDate(capability_notOrOnAfter,
                inner_notOrOnAfter);

        switch (check_CapabilityN_NotOrOnAfter_And_ValuesN1_NotOrOnAfter) {
            case CalendarUtils.AFTER_THAN: {
                String msg = "The 'Not Or On After' date of the created capability ("
                + new SimpleDateFormat(CalendarUtils.DATE_PRINT_PATTERN).format(
                        capability_notOrOnAfter.toGregorianCalendar().getTime())
                + ") is greater than the 'Not Or On After' date of the authorising capability("
                + new SimpleDateFormat(CalendarUtils.DATE_PRINT_PATTERN).format(
                        inner_notOrOnAfter.toGregorianCalendar().getTime()) + ")";
                LOGGER.severe(msg);
                throw new CapabilitySchemaFactoryException(msg);
            }
            case CalendarUtils.BEFORE_THAN: {
                String msg = "The 'Not Or On After' date of the created capability ("
                + new SimpleDateFormat(CalendarUtils.DATE_PRINT_PATTERN).format(
                        capability_notOrOnAfter.toGregorianCalendar().getTime())
                + ") is earlier than the 'Not Or On After' date of the authorising capability ("
                + new SimpleDateFormat(CalendarUtils.DATE_PRINT_PATTERN).format(
                        inner_notOrOnAfter.toGregorianCalendar().getTime()) + ")";
                LOGGER.fine(msg);
                break;
            }
            case CalendarUtils.EQUAL_TO: {
                String msg = "The 'Not Or On After' date of the created capability ("
                + new SimpleDateFormat(CalendarUtils.DATE_PRINT_PATTERN).format(
                        capability_notOrOnAfter.toGregorianCalendar().getTime())
                + ") is 'equal' to the 'Not Or On After' date of the authorising capability ("
                + new SimpleDateFormat(CalendarUtils.DATE_PRINT_PATTERN).format(
                        inner_notOrOnAfter.toGregorianCalendar().getTime()) + ")";
                LOGGER.severe(msg);
                throw new CapabilitySchemaFactoryException(msg);
            }
        }
    }

    private synchronized XMLGregorianCalendar getReallyCapabilityNotBefore(
            XMLGregorianCalendar capability_notBefore_UTC,
            XMLGregorianCalendar capability_issueIstant_UTC) {

        int check_CapabilityN_NotBefore_And_capability_Issue_Istant =
                CalendarUtils.comapareDataToAnotherDate(
                capability_notBefore_UTC, capability_issueIstant_UTC);

        switch (check_CapabilityN_NotBefore_And_capability_Issue_Istant) {
            case CalendarUtils.AFTER_THAN: {
                String msg = "The 'Not Before' Date of the created capability ("
                + new SimpleDateFormat(CalendarUtils.DATE_PRINT_PATTERN).format(
                        capability_notBefore_UTC.toGregorianCalendar().getTime())
                + ") is greater than the issue instant of the authorising capability ("
                + new SimpleDateFormat(CalendarUtils.DATE_PRINT_PATTERN).format(
                        capability_issueIstant_UTC.toGregorianCalendar().getTime()) + ")";

                LOGGER.fine(msg);

                return capability_notBefore_UTC;
            }
            case CalendarUtils.BEFORE_THAN: {
                String msg = "The 'Not Before' date of the created capability ("
                + new SimpleDateFormat(CalendarUtils.DATE_PRINT_PATTERN).format(
                        capability_notBefore_UTC.toGregorianCalendar().getTime())
                + ") is earlier than the issue instant  of the authorising capability ("
                + new SimpleDateFormat(CalendarUtils.DATE_PRINT_PATTERN).format(
                        capability_issueIstant_UTC.toGregorianCalendar().getTime()) + ")";

                LOGGER.fine(msg);

                return capability_issueIstant_UTC;
            }
            case CalendarUtils.EQUAL_TO: {
                String msg = "The 'Not Before' date of the created capability ("
                + new SimpleDateFormat(CalendarUtils.DATE_PRINT_PATTERN).format(
                        capability_notBefore_UTC.toGregorianCalendar().getTime())
                + ") is equal than the issue instant of the authorising capability ("
                + new SimpleDateFormat(CalendarUtils.DATE_PRINT_PATTERN).format(
                        capability_issueIstant_UTC.toGregorianCalendar().getTime()) + ")";

                LOGGER.fine(msg);

                return capability_notBefore_UTC;
            }
            default: {
                return null;
            }
        }
    }
}
